home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Applications / FlyThrough 1.1.2 / src / Source / Vehicle Classes / CVehicle.cp < prev    next >
Encoding:
Text File  |  1997-05-14  |  16.2 KB  |  581 lines  |  [TEXT/CWIE]

  1. //
  2. //    CVehicle.cp
  3. //
  4. //    Maintain a "Vehicle" (camera and two headlights)
  5. //
  6. //    By James Jennings
  7. //    Started July 8, 1996
  8. //
  9.  
  10. #include "CVehicle.h"
  11. #include "CVehicleViewPane.h"
  12. #include "Q3Utilities.h"
  13.  
  14. const MessageT msg_ThrustValueMessage = 'Thru';    // sent by the "Thrust" slider
  15.  
  16. const TQ3CameraPlacement kStart = {    // camera starting position
  17.             { 0, 0, 0 },    // cameraLocation
  18.             { 0, 0, 3 },    // pointOfInterest
  19.             { 0, 1, 0 }        // upVector
  20.         };
  21. const float            kLightOffset = 0.4;
  22. const TQ3Vector3D    kZero = { 0, 0, 0 };
  23. const TQ3Vector3D    kUp = { 0, 1, 0 };
  24. const float            kInitialThrottle = .25; // must match initial slider setting
  25.  
  26. #pragma mark === CSpotLightMaker ===
  27.  
  28. CSpotLight::CSpotLight( TQ3CameraPlacement inPlace, float inOffset )
  29. {
  30.     // set light data
  31.     
  32.     mData.lightData.isOn = kQ3True;
  33.     mData.lightData.brightness = 1.0;
  34.     ::Q3ColorRGB_Set( &mData.lightData.color, 1, 1, 1 );
  35.     
  36.     // set spot light specific stuff
  37.     
  38.     mData.castsShadows = kQ3False;
  39.     mData.attenuation = kQ3AttenuationTypeInverseDistance;
  40.     
  41.     // Default location and direction: Will be moved with transforms later.
  42.     SetInitialPlacement( inPlace, inOffset );
  43.     
  44.     mData.hotAngle = Q3Math_DegreesToRadians( 25 );
  45.     mData.outerAngle = Q3Math_DegreesToRadians( 40 );
  46. #if 1
  47.     // Note: kQ3FallOffTypeExponential is an error to the debugging QD3D 1.0.6.
  48.     mData.fallOff = kQ3FallOffTypeCosine;    
  49. #else
  50.     mData.fallOff = kQ3FallOffTypeLinear;    
  51. #endif
  52. }
  53.  
  54. void CSpotLight::Make()
  55. {
  56.     Q3Forget( mObject );
  57.     mObject = ::Q3SpotLight_New( &mData );
  58.     ThrowIfNil_(mObject);
  59. }
  60.  
  61. void CSpotLight::Transform( TQ3Matrix4x4 &inM )
  62. {
  63.     Get();    // make sure that Make() has been called at least once
  64.     
  65.     ::Q3Point3D_Transform( &mStartLocation, &inM, &mData.location );
  66.     ::Q3Vector3D_Transform( &mStartDirection, &inM, &mData.direction );
  67.     
  68.     TQ3Status status;
  69.     status = ::Q3SpotLight_SetLocation( mObject, &mData.location );
  70.     ThrowIfQ3Fail_( status );
  71.     status = ::Q3SpotLight_SetDirection( mObject, &mData.direction );
  72.     ThrowIfQ3Fail_( status );
  73. }
  74.  
  75. void    CSpotLight::SetInitialPlacement( const TQ3CameraPlacement inPlace, float inOffset )
  76. {
  77.     // Set the light next to the camera pointing in the same direction.
  78.     // This affects the mStartLocation and mStartDirection, 
  79.     // which the transformed mData is calculated from.
  80.     // inOffset is how far to the right of the camera to place the light.
  81.     
  82.     // Find mStartDirection
  83.     ::Q3Point3D_Subtract( &inPlace.pointOfInterest, &inPlace.cameraLocation, &mStartDirection );
  84.     ::Q3Vector3D_Normalize( &mStartDirection, &mStartDirection );
  85.     
  86.     // Place the light slightly offset from the camera placement
  87.     TQ3Vector3D right;
  88.     Q3::Vector3D_CompleteBasis( &mStartDirection, &inPlace.upVector, &right );
  89.     ::Q3Vector3D_Scale( &right, inOffset, &right );
  90.     
  91.     ::Q3Point3D_Vector3D_Add( &inPlace.cameraLocation, &right, &mStartLocation );
  92.     
  93.     // Sync mData and the spotlight with the new placement.
  94.     mData.location = mStartLocation;
  95.     mData.direction = mStartDirection;
  96.     
  97.     if (mObject != nil) {
  98.         // Since mObject isn't made until it's needed, it might not exist yet.
  99.         TQ3Status theStatus;
  100.         theStatus = ::Q3SpotLight_SetLocation( mObject, &mData.location );
  101.         ThrowIfQ3Fail_( theStatus );
  102.         theStatus = ::Q3SpotLight_SetDirection( mObject, &mData.direction );
  103.         ThrowIfQ3Fail_( theStatus );
  104.     }
  105. }
  106.  
  107. void CSpotLight::TurnOn( TQ3Boolean inOn )
  108. {
  109.     // Set the object directly
  110.     TQ3Status status = ::Q3Light_SetState( Get(), inOn );
  111.     ThrowIfQ3Fail_(status);
  112.     
  113.     // Set our local data as well
  114.     mData.lightData.isOn = inOn;
  115. }
  116.  
  117.  
  118. #pragma mark === CVehicle ===
  119.  
  120. CVehicle::CVehicle(CVehicleViewPane    *mViewPane)
  121.     : mViewPane(mViewPane), mCamera(0), mHeadlights(0),
  122.       mLeftLight(kStart, -kLightOffset), 
  123.       mRightLight(kStart, kLightOffset),
  124.       mMergedLights(false), mRollIsStabilized(true), mLocalVertical(kUp),
  125.       mVelocity(kZero), mYawVelocity(0), mPitchVelocity(0), mRollVelocity(0), 
  126.       mThrust(0.2), mThrottle(kInitialThrottle)
  127. {
  128.     // All QD3D objects are created as needed.
  129.     // (This makes the constructor exception safe.)
  130.     
  131.     SetPlacement(kStart);
  132. }
  133.  
  134. CVehicle::~CVehicle()
  135. {
  136.     Q3Forget( mCamera );
  137.     Q3Forget( mHeadlights );
  138. }
  139.  
  140. TQ3CameraObject    CVehicle::GetCamera() const 
  141. {
  142.     if (mCamera==nil) {
  143.         // GetCamera() is in principle const, but since we're defering
  144.         // the creation of the camera, we need to cast away the constantness.
  145.         TQ3CameraObject &camera = const_cast<CVehicle*>(this)->mCamera;
  146.         
  147.         SDimension16 frameSize;
  148.         mViewPane->GetFrameSize( frameSize );
  149.         
  150.         CCameraMaker maker( frameSize );
  151.         camera = maker.GetRef();
  152.         
  153.         TQ3Status theStatus = ::Q3Camera_SetPlacement(camera, &mPlacement);
  154.         ThrowIfQ3Fail_( theStatus );
  155.     }
  156.     
  157.     return mCamera; 
  158. }
  159.  
  160. void    CVehicle::SetCamera(TQ3CameraObject inCamera)
  161. {
  162.     // We don't want an outsider to change our camera type
  163.     // so we only copy those camera properites that are reasonable.
  164.     Assert_(inCamera != nil);
  165.     TQ3Status theStatus;
  166.     
  167.     // We need to pass camera placement data to other structures.
  168.     TQ3CameraPlacement thePlace;
  169.     theStatus = ::Q3Camera_GetPlacement( inCamera, &thePlace );
  170.     Assert_(theStatus == kQ3Success);
  171.     SetPlacement(thePlace);
  172.     
  173. }
  174.  
  175. void    CVehicle::SetPlacement(const TQ3CameraPlacement &inPlace)
  176. {
  177.     // Move the camera to a particular place.
  178.     // We move the start placement to that place and clear the transform.
  179.     
  180.     // Validation might change inPlace, and inPlace is const: Make a local copy.
  181.     TQ3CameraPlacement thePlace = inPlace;
  182.     if ( Q3::CameraPlacement_Validate(&thePlace) == kQ3False ) return;
  183.     
  184.     mStartPlacement = thePlace;
  185.     mPlacement = thePlace;
  186.     ::Q3Matrix4x4_SetIdentity(&mMatrix);    // the transform from mStartPlacement to mPlacement
  187.     
  188.     mLeftLight.SetInitialPlacement(thePlace, (mMergedLights ? 0 : -kLightOffset));
  189.     mRightLight.SetInitialPlacement(thePlace, (mMergedLights ? 0 : kLightOffset));
  190.     
  191.     // Send the camera to the new position.
  192.     if (mCamera!=nil) {
  193.         // Since the camera is only made when needed, it might not exist yet.
  194.         TQ3Status theStatus = ::Q3Camera_SetPlacement( mCamera, &mPlacement );
  195.         ThrowIfQ3Fail_( theStatus );
  196.     }
  197. }
  198.  
  199. void    CVehicle::AddHeadlightsToGroup( TQ3GroupObject ioGroup ) const
  200. {
  201.     // When most of our lights are from an outside source,
  202.     // we still want to be able to add our headlights to it.
  203.     Assert_(ioGroup != nil);
  204.     
  205.     TQ3GroupPosition pos;
  206.     pos = ::Q3Group_AddObject( ioGroup, mLeftLight.Get() );
  207.     ThrowIf_(pos==0);
  208.     
  209.     pos = ::Q3Group_AddObject( ioGroup, mRightLight.Get() );
  210.     ThrowIf_(pos==0);
  211. }
  212.  
  213. void CVehicle::SetLightMode( CommandT inLightMode )
  214. {
  215.     switch( inLightMode ) {
  216.     case cmd_LightsOff:
  217.         mLeftLight.TurnOn( kQ3False ); 
  218.         mRightLight.TurnOn( kQ3False ); 
  219.         break;
  220.     case cmd_LeftLight:
  221.         mLeftLight.TurnOn( kQ3True ); 
  222.         mRightLight.TurnOn( kQ3False ); 
  223.         break;
  224.     case cmd_RightLight:
  225.         mLeftLight.TurnOn( kQ3False ); 
  226.         mRightLight.TurnOn( kQ3True ); 
  227.         break;
  228.     case cmd_BothLights:
  229.         mLeftLight.TurnOn( kQ3True ); 
  230.         mRightLight.TurnOn( kQ3True ); 
  231.         break;
  232.     case cmd_CenteredLights:
  233.         mLeftLight.SetInitialPlacement( mStartPlacement, 0 );
  234.         mLeftLight.Transform( mMatrix );
  235.         mRightLight.SetInitialPlacement( mStartPlacement, 0 );
  236.         mRightLight.Transform( mMatrix );
  237.         mMergedLights = true;
  238.         break;
  239.     case cmd_SeparatedLights:
  240.         mLeftLight.SetInitialPlacement( mStartPlacement, -kLightOffset );
  241.         mLeftLight.Transform( mMatrix );
  242.         mRightLight.SetInitialPlacement( mStartPlacement, kLightOffset );
  243.         mRightLight.Transform( mMatrix );
  244.         mMergedLights = false;
  245.         break;
  246.     default:
  247.         SignalPStr_("\pBad Light Mode");
  248.         break;
  249.     }
  250. }
  251.  
  252. CommandT    CVehicle::GetHeadlightState(void) const
  253. {
  254.     Boolean left = mLeftLight.IsOn();
  255.     Boolean right = mRightLight.IsOn();
  256.     if (left && right) {
  257.         return cmd_BothLights;
  258.     } else if (left) {
  259.         return cmd_LeftLight;
  260.     } else if (right) {
  261.         return cmd_RightLight;
  262.     }
  263.     return cmd_LightsOff;
  264. }
  265.  
  266. #pragma mark === Motion Control ===
  267.  
  268. void    CVehicle::SetScale(float inScale)
  269. {
  270.     // The scale is the scale of the scene, 
  271.     // usually the diagonal of the bounding box.
  272.     // It determines the thrust so that we can navigate
  273.     // the scene in a reasonable amount of time.
  274.     
  275.     // 0.01 is a hand tuned adjustment
  276.     mThrust = inScale * 0.01;
  277.     
  278.     // Adjust the camera range to the scale.
  279.     // (These numbers are rough guesses.)
  280.     // Note: values of scale/1000 and scale*100 got reports of problems
  281.     // in a large "hierarchical trigrid". I think that scale/1000 and scale*10
  282.     // is the least I can get away with.
  283.     TQ3CameraRange range;
  284.     range.hither = inScale/1000;
  285.     range.yon = inScale*15;
  286.     TQ3Status theStatus = ::Q3Camera_SetRange(GetCamera(), &range);
  287.     Assert_(theStatus==kQ3Success);
  288. }
  289.  
  290. Boolean CVehicle::Boost( float inT, Int16 inSignX, Int16 inSignY, Int16 inSignZ )
  291. {    // Accelerate the vehicle in any of the above directions.
  292.     // inT is the number of tick for this iteration
  293.     // inSignX, inSignY, and inSignZ are the signs of the accelerations
  294.     // in each direction.
  295.     // Return true if we move and need to redraw the view.
  296.     Boolean moved = false;
  297.     
  298.     float accel = GetThrust();
  299.     float damping = 0.25;
  300.     float delta;
  301.     
  302.     delta = CalcDynamicParam( inT, mVelocity.x, inSignX, accel, damping );
  303.     if (delta != 0.0) {
  304.         MoveRightBy( delta );
  305.         moved = true;
  306.     }
  307.     
  308.     delta = CalcDynamicParam( inT, mVelocity.y, inSignY, accel, damping );
  309.     if (delta != 0.0) {
  310.         MoveForwardBy( delta );
  311.         moved = true;
  312.     }
  313.     
  314.     delta = CalcDynamicParam( inT, mVelocity.z, inSignZ, accel, damping );
  315.     if (delta != 0.0) {
  316.         MoveUpBy( delta );
  317.         moved = true;
  318.     }
  319.     
  320.     return moved;
  321. }
  322.  
  323. Boolean    CVehicle::Spin( float inT, Int16 inSignY, Int16 inSignP, Int16 inSignR )
  324. {
  325.     Boolean moved = false;
  326.     
  327.     float accel = Q3Math_DegreesToRadians(1);
  328.     float damping = 0.25;
  329.     float delta;
  330.     
  331.     delta = CalcDynamicParam( inT, mYawVelocity, inSignY, accel, damping );
  332.     if (delta != 0.0) {
  333.         YawBy( delta );
  334.         moved = true;
  335.     }
  336.     
  337.     delta = CalcDynamicParam( inT, mPitchVelocity, inSignP, accel, damping );
  338.     if (delta != 0.0) {
  339.         PitchBy( delta );
  340.         moved = true;
  341.     }
  342.     
  343.     delta = CalcDynamicParam( inT, mRollVelocity, inSignR, accel, damping );
  344.     if (delta != 0.0) {
  345.         RollBy( delta );
  346.         moved = true;
  347.     }
  348.     
  349.     return moved;
  350. }
  351.  
  352. float    CVehicle::GetThrust()
  353. {
  354.     // mThrust was determined by a call to SetScale()
  355.     // mThrottle was determined by a user-controlled slider.
  356.     return mThrust * mThrottle;
  357. }
  358.  
  359. float    CVehicle::CalcDynamicParam(float t, float &v, Int16 sign, float a, float damp)
  360. {
  361.     // t = number of ticks this iteration.
  362.     // v = the input and output velocity
  363.     // a = the acceleration/boost
  364.     // sign = sign of the acceleration (depends on modifier keys)
  365.     //    sign == 0: no modifiers
  366.     // damp = between 0 and 1. Bigger --> more breaking when the key is released
  367.     // damp also controls the maximum velocity.
  368.     // Return the amount to move by.
  369.     
  370.     // ••• Uses Newtonian mechanics •••
  371.     // v = v0 + a * t - damping(v,t)
  372.     // d = d0 + v * t
  373.     // The numerical integration is pretty rough, and gets rougher for large t
  374.     // (and t is large when rendering large models) but it's good enough for
  375.     // the user interface.
  376.     
  377.     Assert_( 0.0 < damp && damp <= 1.0 );
  378.     Assert_( t >= 0 );
  379.     
  380.     // Limit the apparent number of ticks between iterations
  381.     // so things don't get too difficult when the machine can't keep up.
  382.     if (t > 6) t = 6;
  383.     
  384.     // We accumulate velocity over time, limited by damping.
  385.     // Damping also slows us down when we release the keys.
  386.     v += sign * a * t;
  387.     for (Int16 i=0; i<t; i++) 
  388.         v *= 1 - damp; // (damping depends on time)
  389.     
  390.     // Special case: slow velocity and no modifier keys. Stop.
  391.     if ( sign == 0 && -a < v && v < a ) 
  392.         v = 0;
  393.     
  394.     return v*t;
  395. }
  396.  
  397. void    CVehicle::SetRollStabilized(Boolean inStabilized)
  398. {
  399.     mRollIsStabilized = inStabilized;
  400.     if (inStabilized) {
  401.         mLocalVertical = mPlacement.upVector;
  402.         // the upVector is supposed to be normalized, but just in case
  403.     //    ::Q3Vector3D_Normalize( &mLocalVertical, &mLocalVertical );
  404.     }
  405. }
  406.  
  407. Boolean    CVehicle::StabilizeRoll()
  408. {
  409.     if (!RollIsStabilized()) return false;
  410.     
  411.     // Keep our view horizontal.
  412.     // A mix of Yaw and Pitch can result in an accidental Roll.
  413.     // Call this function to remove it.
  414.     // Returns true if anything changed.
  415.     
  416.     // GOAL: make sure our left-right axis is orthogonal to mLocalVertical
  417.     // u = old left right axis
  418.     // v = local vertical (the upVector when stabilizing was turn on)
  419.     // u' = u - (u.v)v
  420.     TQ3Vector3D oldRight, newRight, temp;
  421.     GetRightVector(oldRight);
  422.     
  423.     // These asserts are a nice idea, but they're too sensitive to roundoff errors.
  424. //    Assert_( fabs(::Q3Vector3D_Length( &mLocalVertical ) - 1) <= kQ3RealZero );
  425. //    Assert_( fabs(::Q3Vector3D_Length( &oldRight ) - 1) <= kQ3RealZero );
  426.     ::Q3Vector3D_Normalize( &mLocalVertical, &mLocalVertical );    // just in case
  427.     
  428.     float dot = ::Q3Vector3D_Dot( &mLocalVertical, &oldRight );
  429.     
  430.     // If we're already orthogonal, stop.
  431.     if ( fabs(dot) <= kQ3RealZero ) return false;
  432.     
  433.     Assert_( -1 <= dot && dot <= 1 );
  434.     float angle = kQ3Pi/2 - acos(dot);    // angle to rotate by
  435.     
  436.     // Turn off roll stabilization while we roll
  437.     // so that RollBy() won't change mLocalVertical
  438.     // (not necessary at the moment, but it might be sometime)
  439.     mRollIsStabilized = false;
  440.     RollBy(angle);
  441.     mRollIsStabilized = true;
  442.         
  443.     return true;
  444. }
  445.  
  446. void    CVehicle::GetUpVector( TQ3Vector3D &outUp )
  447. {    // 'up' in vehicle coordinates, normalized
  448.     outUp = mPlacement.upVector;
  449.     // It shouldn't be necessary to normalize
  450.     ::Q3Vector3D_Normalize( &outUp, &outUp );
  451. }
  452.  
  453. void    CVehicle::GetForwardVector( TQ3Vector3D &outFwd )
  454. {    // 'Forward' in vehicle coordinates, normalized
  455.     ::Q3Point3D_Subtract( &mPlacement.pointOfInterest, &mPlacement.cameraLocation, &outFwd );
  456.     ::Q3Vector3D_Normalize( &outFwd, &outFwd );
  457. }
  458.  
  459. void    CVehicle::GetRightVector( TQ3Vector3D &outRight )
  460. {    // 'Right' in vehicle coordinates, normalized
  461.     TQ3Vector3D forward;
  462.     GetForwardVector(forward);
  463.     Q3::Vector3D_CompleteBasis( &forward, &mPlacement.upVector, &outRight );
  464. }
  465.  
  466. void    CVehicle::MoveForwardBy( float inDistance )
  467. {
  468.     // calculate the vector to move by
  469.     TQ3Vector3D d;
  470.     GetForwardVector( d );
  471.     ::Q3Vector3D_Scale( &d, inDistance, &d );
  472.     
  473.     TQ3Matrix4x4 M;
  474.     ::Q3Matrix4x4_SetTranslate( &M, d.x, d.y, d.z );
  475.     ApplyMatrix( M );
  476. }
  477.  
  478. void    CVehicle::MoveRightBy( float inDistance )
  479. {
  480.     // calculate the vector to move by
  481.     TQ3Vector3D right;
  482.     GetRightVector( right );
  483.     ::Q3Vector3D_Scale( &right, inDistance, &right );
  484.     
  485.     TQ3Matrix4x4 M;
  486.     ::Q3Matrix4x4_SetTranslate( &M, right.x, right.y, right.z );
  487.     ApplyMatrix( M );
  488. }
  489.  
  490. void    CVehicle::MoveUpBy( float inDistance )
  491. {
  492.     // calculate the vector to move by
  493.     TQ3Vector3D up;
  494.     GetUpVector( up );
  495.     ::Q3Vector3D_Scale( &up, inDistance, &up );
  496.     
  497.     TQ3Matrix4x4 M;
  498.     ::Q3Matrix4x4_SetTranslate( &M, up.x, up.y, up.z );
  499.     ApplyMatrix( M );
  500. }
  501.  
  502. void    CVehicle::YawBy( float inAngle )
  503. {
  504.     TQ3Vector3D v;
  505.     GetUpVector( v );
  506.     
  507.     TQ3Matrix4x4 M;
  508.     ::Q3Matrix4x4_SetRotateAboutAxis( &M, &mPlacement.cameraLocation, &v, inAngle );
  509.     ApplyMatrix( M );
  510. }
  511.  
  512. void    CVehicle::PitchBy( float inAngle )
  513. {
  514.     TQ3Vector3D v;
  515.     GetRightVector( v );
  516.     
  517.     TQ3Matrix4x4 M;
  518.     ::Q3Matrix4x4_SetRotateAboutAxis( &M, &mPlacement.cameraLocation, &v, inAngle );
  519.     ApplyMatrix( M );
  520. }
  521.  
  522. void    CVehicle::RollBy( float inAngle )
  523. {
  524.     // I'd like to "tweak" this for when RollIsStabilized(), but 
  525.     // the things I've tried haven't worked very well.
  526.     // Perhaps it's best to just leave it.
  527.     TQ3Vector3D v;
  528.     GetForwardVector( v );
  529.     
  530.     // Why is mLocalVertical parallel to v?
  531. //    if (RollIsStabilized())    { // project 'forward' to the horizontal plane
  532. //        Q3::Vector3D_MakeOrthogonal( &mLocalVertical, &v, &v );
  533. //    }
  534.     
  535.     TQ3Matrix4x4 M;
  536.     ::Q3Matrix4x4_SetRotateAboutAxis( &M, &mPlacement.cameraLocation, &v, inAngle );
  537.     ApplyMatrix( M );
  538.     
  539. //    if (RollIsStabilized())    { // Adjust the local vertical. How?
  540. //        ::Q3Vector3D_Transform( &mLocalVertical, &M, &mLocalVertical );
  541. //    }
  542. }
  543.  
  544. void    CVehicle::ApplyMatrix( TQ3Matrix4x4 &M )
  545. {
  546.     Assert_( mCamera != nil );
  547.     
  548.     // update and apply the vehicle's transform
  549.     ::Q3Matrix4x4_Multiply( &mMatrix, &M, &mMatrix );
  550.     
  551.     mLeftLight.Transform( mMatrix );
  552.     mRightLight.Transform( mMatrix );
  553.     
  554.     // update and apply the camera's placement
  555.     Q3::CameraPlacement_Transform( &mStartPlacement, &mMatrix, &mPlacement );
  556.     
  557.     TQ3Status status = ::Q3Camera_SetPlacement( mCamera, &mPlacement );
  558.     ThrowIfQ3Fail_( status );
  559.     
  560. }
  561.  
  562. #pragma mark === LListener methods ===
  563.  
  564. void    CVehicle::ListenToMessage(MessageT inMessage, void *ioParam)
  565. {
  566.     const float minThrottle = 0.01;
  567.     const float maxThrottle = 1.0;
  568.     const Int32 maxSlider = 100;    // minSlider = 0
  569.     
  570.     switch (inMessage) {
  571.     case msg_ThrustValueMessage:
  572.         // convert the slider setting to a throttle value
  573.         Int32 theValue = *(Int32*)ioParam;
  574.         mThrottle = minThrottle + ((float)theValue) * (maxThrottle - minThrottle) / maxSlider;
  575.         Assert_( mThrottle >= minThrottle && mThrottle <= maxThrottle );
  576.         break;
  577.     default:
  578.         break;
  579.     }
  580. }
  581.